/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda - http://marcus.cuda.net
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace dnAnalytics.Math
{
    ///<summary>A complex number data type.</summary>
    ///<remarks>See <see cref="ComplexMath">ComplexMath</see> for complex math functions.</remarks>
    ///<seealso cref="ComplexMath"/>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct Complex : IFormattable, IEquatable<Complex>
    {
        private static readonly Complex sI = new Complex(0, 1);
        private static readonly Complex sInfinity = new Complex(Double.PositiveInfinity, Double.PositiveInfinity);
        private static readonly Complex sNaN = new Complex(Double.NaN, Double.NaN);
        private static readonly Complex sOne = new Complex(1);
        private static readonly Complex sZero = new Complex(0);

        private readonly double mReal;
        private readonly double mImag;


        /// <summary>
        /// Creates a complex number from a real number (the imaginary component is set to
        /// <strong>zero</strong>).
        /// </summary>
        /// <param name="real">The real component of the complex number.</param>
        public Complex(double real) : this(real, 0) {}

        /// <summary>Creates a complex number.</summary>
        /// <param name="real">The real component of the complex number.</param>
        /// <param name="imaginary">The imaginary component of the complex number.</param>
        public Complex(double real, double imaginary)
        {
            mReal = real;
            mImag = imaginary;
        }

        /// <summary>
        /// Created a complex number from the given string. The string can be in the
        /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)',
        /// or '(n,ni)', where n is a real number.
        /// </summary>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="value">The string to create the complex number from.</param>
        public Complex(string value) : this(value, null) {}

        /// <summary>
        /// Created a complex number from the given string. The string can be in the
        /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)',
        /// or '(n,ni)', where n is a real number.
        /// </summary>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="value">The string to create the complex number from.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public Complex(string value, IFormatProvider formatProvider)
        {
            this = Parse(value, formatProvider);
        }

        /// <summary>A constant value representing the number one as a complex number.</summary>
        public static Complex One
        {
            get { return sOne; }
        }

        /// <summary>A constant value representing the number zero as a complex number.</summary>
        public static Complex Zero
        {
            get { return sZero; }
        }

        /// <summary>A constant value representing not a number (NaN) as a complex number.</summary>
        public static Complex NaN
        {
            get { return sNaN; }
        }

        /// <summary>A constant value representing infinity as a complex number.</summary>
        public static Complex Infinity
        {
            get { return sInfinity; }
        }

        /// <summary>A constant value representing the complex number I.</summary>
        public static Complex I
        {
            get { return sI; }
        }

        /// <summary>A read-only property to access the real component of complex number.</summary>
        public double Real
        {
            get { return mReal; }
        }

        /// <summary>A read-only property to access the imaginary component of complex number.</summary>
        public double Imaginary
        {
            get { return mImag; }
        }

        /// <summary>Returns the conjugate of a complex number.</summary>
        public Complex Conjugate
        {
            get { return ComplexMath.Conjugate(this); }
        }

        /// <summary>Returns the Absolute of a complex number.</summary>
        public double Absolute
        {
            get { return ComplexMath.Absolute(this); }
        }

        /// <summary>Implicitly converts a double to a complex number.</summary>
        /// <returns>A complex number containing the value of the given double.</returns>
        /// <param name="value">The double value to convert.</param>
        public static implicit operator Complex(double value)
        {
            return new Complex(value);
        }

        /// <summary>Converts a Complex32 to a Complex number.</summary>
        /// <returns>A Complex number containing the value of the given Complex32.</returns>
        /// <param name="value">The Complex32 value to convert.</param>
        public static Complex ToComplex(double value)
        {
            return new Complex(value);
        }

        /// <summary>Implicitly converts a Complex32 to a Complex number.</summary>
        /// <returns>A Complex number containing the value of the given Complex32.</returns>
        /// <param name="value">The Complex32 value to convert.</param>
        public static implicit operator Complex(Complex32 value)
        {
            return new Complex(value.Real, value.Imaginary);
        }

        /// <summary>Converts a Complex32 to a Complex number.</summary>
        /// <returns>A Complex number containing the value of the given Complex32.</returns>
        /// <param name="value">The Complex32 value to convert.</param>
        public static Complex ToComplex(Complex32 value)
        {
            return new Complex(value.Real, value.Imaginary);
        }

        /// <summary>
        /// Checks if two complex numbers are equal. Two complex numbers are equal if their
        /// corresponding real and imaginary components are equal.
        /// </summary>
        /// <returns>
        /// Returns true if the two objects are the same object, or if their corresponding
        /// real and imaginary components are equal, false otherwise.
        /// </returns>
        /// <param name="other">The complex number to compare to with.</param>
        public bool Equals(Complex other)
        {
            return Real == other.Real && Imaginary == other.Imaginary;
        }

        /// <summary>A string representation of this complex number.</summary>
        /// <returns>
        /// The string representation of this complex number formatted as specified by the
        /// format string and format provider.
        /// </returns>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="format">A format specification.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public String ToString(string format, IFormatProvider formatProvider)
        {
            if (IsNaN(this))
            {
                return "NaN";
            }
            if (IsInfinity(this))
            {
                return "Infinity";
            }

            StringBuilder ret = new StringBuilder();

            ret.Append(mReal.ToString(format, formatProvider));
            if (mImag < 0)
            {
                ret.Append(" ");
            }
            else
            {
                ret.Append(" + ");
            }
            ret.Append(mImag.ToString(format, formatProvider)).Append("i");

            return ret.ToString();
        }

        /// <summary>The hash code for the the complex number.</summary>
        /// <returns>The hash code of the complex number.</returns>
        /// <remarks>
        /// The hash code is calculated as
        /// System.Math.Exp(ComplexMath.Absolute(complexNumber)).
        /// </remarks>
        public override int GetHashCode()
        {
            return (int) System.Math.Exp(ComplexMath.Absolute(this));
        }

        /// <summary>
        /// Checks if two complex numbers are equal. Two complex numbers are equal if their
        /// corresponding real and imaginary components are equal.
        /// </summary>
        /// <returns>
        /// Returns true if the two objects are the same object, or if their corresponding
        /// real and imaginary components are equal, false otherwise.
        /// </returns>
        /// <param name="obj">The complex number to compare to with.</param>
        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj is Complex)
            {
                Complex rightSide = (Complex) obj;
                return Real == rightSide.Real && Imaginary == rightSide.Imaginary;
            }
            return false;
        }

        /// <summary>Equality operator to compare two complex numbers.</summary>
        /// <remarks>Returns false if the two variables are not equals using Equals function</remarks>
        /// <returns>
        /// True if the corresponding real and imaginary components of the two complex
        /// numbers are equal; otherwise false.
        /// </returns>
        /// <param name="o1">One of the two complex numbers to compare.</param>
        /// <param name="o2">The other complex number to compare.</param>
        public static bool operator ==(Complex o1, Complex o2)
        {
            return o1.Equals(o2);
        }

        /// <summary>Inequality operator to compare a complex numbers.</summary>
        /// <remarks>Returns false if the two variables are equal using Equals function</remarks>
        /// <returns>
        /// False if the corresponding real and imaginary components of the two complex
        /// numbers are equal; otherwise true.
        /// </returns>
        /// <param name="o1">One of the two complex numbers to compare.</param>
        /// <param name="o2">The other complex number to compare.</param>
        public static bool operator !=(Complex o1, Complex o2)
        {
            return !o1.Equals(o2);
        }

        /// <summary>
        /// Unary plus operator. Added for completion and just returns the same
        /// object.
        /// </summary>
        /// <returns>The same value that is passed to the method.</returns>
        /// <param name="value">The value to return.</param>
        public static Complex operator +(Complex value)
        {
            return value;
        }

        /// <summary>Unary plus. Added for completion and just returns the same object.</summary>
        /// <returns>The same value that is passed to the method.</returns>
        /// <param name="value">The value to return.</param>
        public static Complex Plus(Complex value)
        {
            return value;
        }

        /// <summary>Addition operator. Adds two complex numbers together.</summary>
        /// <returns>The result of the addition.</returns>
        /// <param name="leftSide">One of the complex numbers to add.</param>
        /// <param name="rightSide">The other complex numbers to add.</param>
        public static Complex operator +(Complex leftSide, Complex rightSide)
        {
            return new Complex(leftSide.Real + rightSide.Real, leftSide.Imaginary + rightSide.Imaginary);
        }

        /// <summary>Adds a complex number to this one.</summary>
        /// <returns>The result of the addition.</returns>
        /// <param name="other">The complex number to add to.</param>
        public Complex Add(Complex other)
        {
            return this + other;
        }

        /// <summary>Unary negation operator. Returns the negation of a complex number.</summary>
        /// <returns>The negated value.</returns>
        /// <param name="value">The complex number to negate.</param>
        public static Complex operator -(Complex value)
        {
            return new Complex(-value.Real, -value.Imaginary);
        }

        /// <summary>Subtraction operator. Subtracts two complex numbers.</summary>
        /// <returns>The result of the subtraction.</returns>
        /// <param name="leftSide">The complex number to subtract from.</param>
        /// <param name="rightSide">The complex number to subtract.</param>
        public static Complex operator -(Complex leftSide, Complex rightSide)
        {
            return new Complex(leftSide.Real - rightSide.Real, leftSide.Imaginary - rightSide.Imaginary);
        }

        /// <summary>Subtracts a complex number for this one.</summary>
        /// <returns>The result of the subtraction.</returns>
        /// <param name="other">The complex number to subtract.</param>
        public Complex Subtract(Complex other)
        {
            return this - other;
        }

        /// <summary>Multiplication operator. Multiplies two complex numbers.</summary>
        /// <returns>The result of the multiplication.</returns>
        /// <param name="leftSide">One of the complex numbers to multiply.</param>
        /// <param name="rightSide">The other complex number to multiply.</param>
        public static Complex operator *(Complex leftSide, Complex rightSide)
        {
            return
                new Complex(leftSide.Real*rightSide.Real - leftSide.Imaginary*rightSide.Imaginary,
                            leftSide.Real*rightSide.Imaginary + leftSide.Imaginary*rightSide.Real);
        }

        /// <summary>Multiplies a complex number with this one.</summary>
        /// <returns>The result of the multiplication.</returns>
        /// <param name="other">The complex number to multiply with.</param>
        public Complex Multiply(Complex other)
        {
            return this*other;
        }

        /// <summary>Division operator. Divides a complex number by another.</summary>
        /// <returns>The result of the division.</returns>
        /// <param name="leftSide">The dividend.</param>
        /// <param name="rightSide">The division.</param>
        public static Complex operator /(Complex leftSide, Complex rightSide)
        {
            if (rightSide.mReal == 0.0 && rightSide.Imaginary == 0.0)
            {
                return sNaN;
            }

            if (IsInfinity(rightSide) && !IsInfinity(leftSide))
            {
                return sZero;
            }

            double real, imag;
            if (System.Math.Abs(rightSide.mReal) >= System.Math.Abs(rightSide.mImag))
            {
                double temp = rightSide.mImag/rightSide.mReal;
                double denom = rightSide.mReal + temp*rightSide.mImag;
                real = (leftSide.mReal + temp*leftSide.mImag)/denom;
                imag = (leftSide.mImag - temp*leftSide.mReal)/denom;
            }
            else
            {
                double temp = rightSide.mReal/rightSide.mImag;
                double denom = rightSide.mImag + temp*rightSide.mReal;
                real = (leftSide.mReal*temp + leftSide.mImag)/denom;
                imag = (leftSide.mImag*temp - leftSide.mReal)/denom;
            }
            return new Complex(real, imag);
        }

        /// <summary>Divides this complex number by another one.</summary>
        /// <returns>The result of the division.</returns>
        /// <param name="other">The complex number to divide this one with.</param>
        public Complex Divide(Complex other)
        {
            return this/other;
        }

        ///<summary>Tests whether the the complex number is not a number.</summary>
        ///<returns>True if either the real or imaginary components are NaN, false otherwise.</returns>
        public static bool IsNaN(Complex value)
        {
            if (value == sNaN)
            {
                return true;
            }
            return (Double.IsNaN(value.mReal) || Double.IsNaN(value.mImag));
        }

        ///<summary>Tests whether the the complex number is infinite.</summary>
        ///<returns>True if either the real or imaginary components are infinite, false otherwise.</returns>
        public static bool IsInfinity(Complex value)
        {
            if (value == sInfinity)
            {
                return true;
            }
            return (Double.IsInfinity(value.mReal) || Double.IsInfinity(value.mImag));
        }

        /// <summary>A string representation of this complex number.</summary>
        /// <returns>The string representation of this complex number.</returns>
        public override string ToString()
        {
            return ToString(null, null);
        }

        /// <summary>A string representation of this complex number.</summary>
        /// <returns>
        /// The string representation of this complex number formatted as specified by the
        /// format string.
        /// </returns>
        /// <param name="format">A format specification.</param>
        public string ToString(string format)
        {
            return ToString(format, null);
        }

        /// <summary>A string representation of this complex number.</summary>
        /// <returns>
        /// The string representation of this complex number formatted as specified by the
        /// format provider.
        /// </returns>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public string ToString(IFormatProvider formatProvider)
        {
            return ToString(null, formatProvider);
        }

        /// <summary>
        /// Creates a complex number based on a string. The string can be in the following
        /// formats(without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)', or
        /// '(n,ni)', where n is a real number.
        /// </summary>
        /// <returns>A complex number containing the value specified by the given string.</returns>
        /// <param name="value">The string to parse.</param>
        public static Complex Parse(String value)
        {
            return Parse(value, null);
        }

        /// <summary>
        /// Creates a complex number based on a string. The string can be in the following
        /// formats(without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)', or
        /// '(n,ni)', where n is a real number.
        /// </summary>
        /// <returns>A complex number containing the value specified by the given string.</returns>
        /// <param name="value">the string to parse.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public static Complex Parse(String value, IFormatProvider formatProvider)
        {
            if (value == null)
            {
                throw new ArgumentNullException(value);
            }
            value = value.Trim();
            if (value.Length == 0)
            {
                throw new FormatException();
            }

            //check if one character strings are valid
            if (value.Length == 1)
            {
                if (String.Compare(value, "i", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return new Complex(0, 1);
                }
                return new Complex(Double.Parse(value, formatProvider));
            }

            //strip out parens
            if (value.StartsWith("(", StringComparison.Ordinal))
            {
                if (!value.EndsWith(")", StringComparison.Ordinal))
                {
                    throw new FormatException();
                }
                value = value.Substring(1, value.Length - 2);
            }

            string real = value;
            string imag = "0";

            //comma separated
            int index = value.IndexOf(',');
            if (index > -1)
            {
                real = value.Substring(0, index);
                imag = value.Substring(index + 1, value.Length - index - 1);
            }
            else
            {
                index = value.IndexOf('+', 1);
                if (index > -1)
                {
                    real = value.Substring(0, index);
                    imag = value.Substring(index + 1, value.Length - index - 1);
                }
                else
                {
                    index = value.IndexOf('-', 1);
                    if (index > -1)
                    {
                        real = value.Substring(0, index);
                        imag = value.Substring(index, value.Length - index);
                    }
                }
            }

            //see if we have numbers in the format xxxi
            if (real.EndsWith("i", StringComparison.OrdinalIgnoreCase))
            {
                if (!imag.Equals("0", StringComparison.Ordinal))
                {
                    throw new FormatException();
                }
                imag = real.Substring(0, real.Length - 1);
                real = "0";
            }
            if (imag.EndsWith("i", StringComparison.OrdinalIgnoreCase))
            {
                imag = imag.Substring(0, imag.Length - 1);
            }
            //handle cases of - n, + n
            if (real.StartsWith("-", StringComparison.Ordinal))
            {
                real = "-" + real.Substring(1, real.Length - 1).Trim();
            }
            if (imag.StartsWith("-", StringComparison.Ordinal))
            {
                imag = "-" + imag.Substring(1, imag.Length - 1).Trim();
            }

            Complex ret;
            try
            {
                ret = new Complex(Double.Parse(real.Trim(), formatProvider), Double.Parse(imag.Trim(), formatProvider));
            }
            catch (Exception)
            {
                throw new FormatException();
            }
            return ret;
        }

        /// <summary>
        /// Converts the string representation of a complex number to a double-precision complex number equivalent. 
        /// A return value indicates whether the conversion succeeded or failed.
        /// </summary>
        /// <param name="value">A string containing a complex number to convert. </param>
        /// <param name="result"></param>
        /// <returns>If the conversion succeeds, the result will contain a complex number equivalent to value. 
        /// Otherwise the result will contain complex32.Zero.  This parameter is passed uninitialized</returns>
        public static bool TryParse(String value, out Complex result)
        {
            return TryParse(value, null, out result);
        }

        /// <summary>
        /// Converts the string representation of a complex number to double-precision complex number equivalent. 
        /// A return value indicates whether the conversion succeeded or failed.
        /// </summary>
        /// <param name="value">A string containing a complex number to convert. </param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information about value. </param>
        /// <param name="result"></param>
        /// <returns>If the conversion succeeds, the result will contain a complex number equivalent to value. 
        /// Otherwise the result will contain complex32.Zero.  This parameter is passed uninitialized</returns>
        public static bool TryParse(String value, IFormatProvider formatProvider, out Complex result)
        {
            bool ret;
            try
            {
                result = Parse(value, formatProvider);
                ret = true;
            }
            catch (ArgumentNullException)
            {
                result = sZero;
                ret = false;
            }
            catch (FormatException)
            {
                result = sZero;
                ret = false;
            }
            return ret;
        }
    }
}